home *** CD-ROM | disk | FTP | other *** search
/ IRIX 6.2 Applications 1996 May / SGI IRIX 6.2 Applications 1996 May.iso / dist / impr_dev.idb / usr / impressario / src / scan / template_driver / main.c.z / main.c
C/C++ Source or Header  |  1996-05-06  |  39KB  |  1,614 lines

  1. /*
  2.  * main.c
  3.  *
  4.  *     main routine for the scanner drivers, scanner callback
  5.  *     functions.
  6.  *
  7.  *     This file should NOT be modified to support new scanners!!
  8.  * 
  9.  * Copyright 1992, 1993, 1994 Silicon Graphics, Inc.
  10.  * All Rights Reserved.
  11.  *
  12.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  13.  * the contents of this file may not be disclosed to third parties, copied or
  14.  * duplicated in any form, in whole or in part, without the prior written
  15.  * permission of Silicon Graphics, Inc.
  16.  *
  17.  * RESTRICTED RIGHTS LEGEND:
  18.  * Use, duplication or disclosure by the Government is subject to restrictions
  19.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  20.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  21.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  22.  * rights reserved under the Copyright Laws of the United States.
  23.  */
  24.  
  25. #include <sys/types.h>
  26. #include <sys/prctl.h>
  27. #include <sys/wait.h>
  28.  
  29. #include <unistd.h>
  30. #include <stdio.h>
  31. #include <signal.h>
  32. #include <errno.h>
  33. #include <dslib.h>
  34. #include <malloc.h>
  35. #include <stdlib.h>
  36. #include <invent.h>
  37. #include <bstring.h>
  38. #include <ulocks.h>
  39. #include <string.h>
  40. #include <locale.h>
  41.  
  42. #include <msgs/uxsgiimpr.h>
  43.  
  44. #include <scanner.h>
  45. #include <scanipc.h>
  46. #include <scandrv.h>
  47. #include <scanconv.h>
  48.  
  49. #include "scan.h"
  50.  
  51. /*
  52.  * Since the units of resolution are in the denominator, going from
  53.  * inches to centimeters and back is the inverse of what it is when
  54.  * the units are in the numerator.
  55.  */
  56. #define RESINCHTOCM(inch) CMTOINCH(inch)
  57. #define RESCMTOINCH(cm) INCHTOCM(cm)
  58.  
  59. #define ISRGBPIX8(t) ((t)->packing == SC_PACKPIX && \
  60.               (t)->channels == 3 && \
  61.               (t)->type == SC_RGB && \
  62.               (t)->bpp == 8)
  63.  
  64. #define ISRGBPIX16(t) ((t)->packing == SC_PACKPIX && \
  65.                (t)->channels == 3 && \
  66.                (t)->type == SC_RGB && \
  67.                (t)->bpp > 8)
  68.  
  69. #define ISRGBPIX1(t) ((t)->packing == SC_PACKPIX && \
  70.               (t)->channels == 3 && \
  71.               (t)->type == SC_RGB && \
  72.               (t)->bpp == 1)
  73.  
  74. #define ISRGBPLANE8(t) ((t)->packing == SC_PACKPLANE && \
  75.             (t)->channels == 3 && \
  76.             (t)->type == SC_RGB && \
  77.             (t)->bpp == 8)
  78.  
  79. #define ISRGBPLANE16(t) ((t)->packing == SC_PACKPLANE && \
  80.              (t)->channels == 3 && \
  81.              (t)->type == SC_RGB && \
  82.              (t)->bpp > 8)
  83.  
  84. #define ISGREY8(t) ((t)->packing == SC_PACKPIX && \
  85.             (t)->channels == 1 && \
  86.             (t)->type == SC_GREY && \
  87.             (t)->bpp == 8)
  88.  
  89. #define ISGREY16(t) ((t)->packing == SC_PACKPIX && \
  90.              (t)->channels == 1 && \
  91.              (t)->type == SC_GREY && \
  92.              (t)->bpp > 8)
  93.  
  94. #define ISMONO1(t) ((t)->packing == SC_PACKPIX && \
  95.                (t)->channels == 1 && \
  96.                (t)->type == SC_MONO && \
  97.                (t)->bpp == 1)
  98.  
  99.  
  100. int drverr;
  101. char *drvmsg;
  102.  
  103. extern int mpin(void *addr, unsigned len);
  104. extern int munpin(void *addr, unsigned len);
  105.  
  106. static pid_t scanpid, imgpid;       /* child process ids */
  107. static SCANINFO *scan;              /* info about the scanner */
  108. static SCANPARAMS sparams;          /* info about the scan to take place */
  109. static char *scanbuf;               /* memory to buffer scan lines */
  110. static unsigned bytesPinned = 0;    /* Number of bytes pinned into */
  111.                     /* memory */
  112. static long maxmem;                 /* max memory to allocat for */
  113.                     /* scanbuf */
  114. static long xpixels, xbytes, ysize; /* The size of the scan data we */
  115.                     /* will be returning to the */
  116.                     /* scanning application.  The */
  117.                     /* fields of sparams describes the */
  118.                     /* size of the scan data that the */
  119.                     /* scan module will be returning */
  120.                     /* to us */
  121. static char *ibuf = NULL;           /* Image processing line buffer */
  122. static int *zmap = NULL;            /* Zoom map */
  123. static SCSTATUS status;             /* Current scan status */
  124. static int kids = 0;                /* Number of child processes */
  125.                     /* running */
  126.  
  127. /*
  128.  * Lin conversion function for DoImgProc
  129.  */
  130. static void (*convert)(void *from, int fx, void *to, int tx, int *z);
  131.  
  132. /*
  133.  *  static void
  134.  *  ScanCleanup(void)
  135.  *
  136.  *  Description:
  137.  *      Free all the resources used by a scan, preparing things for a
  138.  *      subsequent scanning operation.
  139.  *
  140.  *      This should not be called from a signal handler (specifically,
  141.  *      childdeath); use SCDriverSyncFunction.  It should never be
  142.  *      called if either of the child processes are still around.
  143.  */
  144.  
  145. static void
  146. ScanCleanup(void)
  147. {
  148.     int statusPut = 0;
  149.     /*
  150.      * Get rid of the queues and free memory
  151.      */
  152.     if (sparams.scanq) {
  153.     SCDestroyQueue(sparams.scanq);
  154.     sparams.scanq = NULL;
  155.     }
  156.  
  157.     if (sparams.sfreeq) {
  158.     SCDestroyQueue(sparams.sfreeq);
  159.     sparams.sfreeq = NULL;
  160.     }
  161.  
  162.     if (scanbuf) {
  163.     if (bytesPinned) {
  164.         if (munpin(scanbuf, bytesPinned) < 0) {
  165. #ifdef DEBUG
  166.         perror("munpin");
  167. #endif
  168.         }
  169.         bytesPinned = 0;
  170.     }
  171.     free(scanbuf);
  172.     scanbuf = NULL;
  173.     }
  174.  
  175.     if (ibuf) {
  176.     free(ibuf);
  177.     ibuf = NULL;
  178.     }
  179.  
  180.     if (zmap) {
  181.     SCDestroyZoomMap(zmap);
  182.     zmap = NULL;
  183.     }
  184.  
  185.     if (status.state == SC_ERROR) {
  186.     SCDriverPutStatus(&status,
  187.               status.errno == SCEDRVMSG ? drvmsg : NULL);
  188.     statusPut = 1;
  189.     }
  190.  
  191.     if (status.state != SC_READY) {
  192.     status.state = SC_READY;
  193.     status.curline = 0;
  194.     status.pass = 0;
  195.     if (!statusPut) {
  196.         SCDriverPutStatus(&status, NULL);
  197.     }
  198.     (void)ScanReset(scan);
  199.     }
  200. }
  201.  
  202. /*
  203.  *  static void
  204.  *  EndScanning(void)
  205.  *
  206.  *  Description:
  207.  *      Tell the child processes that are doing the scanning and
  208.  *      converting to exit.  SCQueueSetExit causes processes that call
  209.  *      SCEnqueue or SCDequeue on that queue to exit, and
  210.  *      SCDriverStopWriter causes processes that call SCDriverPutRow
  211.  *      to exit.
  212.  */
  213.  
  214. static void
  215. EndScanning(void)
  216. {
  217.     if (sparams.scanq) {
  218.     SCQueueSetExit(sparams.scanq);
  219.     }
  220.  
  221.     if (sparams.sfreeq) {
  222.     SCQueueSetExit(sparams.sfreeq);
  223.     }
  224.  
  225.     SCDriverStopWriter(imgpid);
  226. }
  227.  
  228. /*
  229.  *  static void
  230.  *  childdeath(int sig)
  231.  *
  232.  *  Description:
  233.  *      Either scanproc or imgproc exited.  wait() to find the exit
  234.  *      status; if exit status was non-zero, then drverr contains a
  235.  *      suitable error code and we set the status indicate that an
  236.  *      error occurred.  If the process died due to a signal, we don't
  237.  *      really know what happened so we'll call it a device error.
  238.  *
  239.  *  Parameters:
  240.  *      sig  - signal that caused us to be called (SIGCLD)
  241.  */
  242.  
  243. /*ARGSUSED*/
  244. static void
  245. childdeath(int sig)
  246. {
  247.     pid_t pid;
  248.     int stat;
  249.  
  250.     for (pid = waitpid(-1, &stat, WNOHANG);
  251.      pid > 0;
  252.      pid = waitpid(-1, &stat, WNOHANG)) {
  253.     kids--;
  254.     if (status.state == SC_SCANNING &&
  255.         (WIFEXITED(stat) && WEXITSTATUS(stat) != 0
  256.          || WIFSIGNALED(stat))) {
  257.         if (WIFSIGNALED(stat)) {
  258.         drverr = SCEDEV;
  259.         }
  260.         /*
  261.          * Don't actually call SCDriverPutStatus here, because
  262.          * that can lead to deadlock.  ScanCleanup should notice
  263.          * that an error has occurred and set status
  264.          * appropriately (but it doesn't right now).
  265.          */
  266.         status.state = SC_ERROR;
  267.         status.errno = drverr;
  268.         /*
  269.          * Call EndScanning synchronously.  This will have the
  270.          * effect of ending the other child process.  When the
  271.          * other child process has exited, we'll end up in this
  272.          * signal handler again, kids will be 0, and we'll cause
  273.          * ScanCleanup to be called.
  274.          */
  275.         SCDriverSyncFunction((void (*)(void *))EndScanning, NULL);
  276.     }
  277.     /*
  278.      * Once they're both dead, do the cleanup
  279.      */
  280.     if (kids == 0) {
  281.         SCDriverSyncFunction((void (*)(void *))ScanCleanup, NULL);
  282.     }
  283.     }
  284. }
  285.  
  286. /*
  287.  *  void
  288.  *  ExitCleanup(void)
  289.  *
  290.  *  Description:
  291.  *      Abort a scan if one is in progress, since we're about to exit.
  292.  *      We can't just call AbortScan, because it relies on processing
  293.  *      of SIGCHLD and SCDriverSyncFunction to call ScanCleanup.
  294.  *      We'll never end up back in SCDriverMainLoop() after this, so
  295.  *      we have to take care of everything inline.
  296.  */
  297.  
  298. void
  299. ExitCleanup(void)
  300. {
  301.     sigset_t mask, omask;
  302.     int stat;
  303.  
  304.     if (status.state == SC_SCANNING) {
  305.     (void)sigemptyset(&mask);
  306.     (void)sigaddset(&mask, SIGCHLD);
  307.     (void)sigprocmask(SIG_BLOCK, &mask, &omask);
  308.  
  309.     EndScanning();
  310.  
  311.     (void)waitpid(scanpid, &stat, 0);
  312.     (void)waitpid(imgpid, &stat, 0);
  313.  
  314.     (void)ScanCleanup();
  315.  
  316.     (void)sigprocmask(SIG_SETMASK, &omask, 0);
  317.     }
  318. }
  319.  
  320. /*
  321.  *  static void
  322.  *  hangup(int sig)
  323.  *
  324.  *  Description:
  325.  *      SIGHUP handler.  If our parent has exited, we should exit too.
  326.  *
  327.  *  Parameters:
  328.  *      sig  SIGHUP
  329.  */
  330.  
  331. /*ARGSUSED*/
  332. static void
  333. hangup(int sig)
  334. {
  335.     if (getppid() == 1) {
  336.     ExitCleanup();
  337.     exit(0);
  338.     }
  339. }
  340.  
  341. /*
  342.  *  static void
  343.  *  SetMaxMem(void)
  344.  *
  345.  *  Description:
  346.  *      Set maxmem, the upper limit on how much memory we'll malloc to
  347.  *      do scanning.
  348.  */
  349.  
  350. static void
  351. SetMaxMem(void)
  352. {
  353.     inventory_t *inv;
  354.  
  355.     if (setinvent() == 0) {
  356.     while ((inv = getinvent()) != NULL) {
  357.         if (inv->inv_class == INV_MEMORY && inv->inv_type == INV_MAIN) {
  358.         /*
  359.          * Don't use more than 1/3 of main memory
  360.          */
  361.         maxmem = inv->inv_state / 3;
  362.         endinvent();
  363.         return;
  364.         }
  365.     }
  366.     endinvent();
  367.     }
  368.     /*
  369.      * Default to 2 meg
  370.      */
  371.     maxmem = 2 * 1024 * 1024;
  372. }
  373.  
  374. /*
  375.  *  void
  376.  *  scanfunc(int cmd, SCARG *arg, SCRES *res)
  377.  *
  378.  *  Description:
  379.  *      What follows are many similar functions that implement the
  380.  *      server side of the scanning protocol.  These commands are
  381.  *      called from within SCDriverMainLoop(), which knows which
  382.  *      function to call for a given command because we passed in
  383.  *      scanTable.  The order of the functions in scanTable is very
  384.  *      important; the index in the table of each function must be the
  385.  *      same as its SCN_ #define in <scanipc.h>.
  386.  *
  387.  *      Each function gets called with its SCN_ #define as the first
  388.  *      argument, the argument data as the second argument, and a
  389.  *      result structure as the third argument.  In general, each
  390.  *      function casts the argument data (arg->data) to the
  391.  *      appropriate type, and executes appropriately, making res->data
  392.  *      point to a buffer containing the results.
  393.  *
  394.  *      In the event of an error, the function should set res->errno
  395.  *      to a value either from <errno.h> or from <scanner.h> and
  396.  *      return.  The library will report an error to the application
  397.  *      if res->errno is non-zero.
  398.  *
  399.  *      The library initializes all fields of the SCRES structure
  400.  *      pointed to by res to 0 before calling each function.
  401.  *
  402.  *  Parameters:
  403.  *      cmd  SCN_ #define for the command
  404.  *      arg  argument structure
  405.  *      res  results structure
  406.  */
  407.  
  408. /*
  409.  *  static void
  410.  *  InitOK(int cmd, SCARG *arg, SCRES *res)
  411.  *
  412.  *  Description:
  413.  *      Make sure driver initialized ok
  414.  */
  415.  
  416. /*ARGSUSED*/
  417. static void
  418. InitOK(int cmd, SCARG *arg, SCRES *res)
  419. {
  420.     res->errno = drverr;
  421.     if (drverr == SCEDRVMSG) {
  422.     res->errMsg = drvmsg;
  423.     }
  424.     res->len = 0;
  425.     res->free = 0;
  426. }
  427.  
  428. /*
  429.  *  static void
  430.  *  Die(int cmd, SCARG *arg, SCRES *res)
  431.  *
  432.  *  Description:
  433.  *      Obey the scanner application's order to die
  434.  */
  435.  
  436. /*ARGSUSED*/
  437. static void
  438. Die(int cmd, SCARG *arg, SCRES *res)
  439. {
  440.     ExitCleanup();
  441.     exit(0);
  442. }
  443.  
  444. /*
  445.  *  static void
  446.  *  ResMinMax(int cmd, SCARG *arg, SCRES *res)
  447.  *
  448.  *  Description:
  449.  *      Inform the scanner application of the minimum and maximum
  450.  *      values for the horizontal and vertical resolutions.  Convert
  451.  *      between metrics as necessary.
  452.  *
  453.  *  Parameters:
  454.  *      cmd  SCN_MINMAXRES
  455.  *      arg  metric
  456.  *      res  SCMINMAXRES
  457.  */
  458.  
  459. /*ARGSUSED*/
  460. static void
  461. ResMinMax(int cmd, SCARG *arg, SCRES *res)
  462. {
  463.     static SCMINMAXRES r;
  464.     /*
  465.      * Test arg->data first so as not to break compatibility with
  466.      * applications built with old buggy library.
  467.      */
  468.     int metric = arg->data ? *(int *)arg->data : SC_INCHES;
  469.  
  470.     if (metric == scan->metric) {
  471.     r.maxx = scan->maxxres;
  472.     r.maxy = scan->maxyres;
  473.     } else if (metric == SC_INCHES) {
  474.     r.maxx = RESCMTOINCH(scan->maxxres);
  475.     r.maxy = RESCMTOINCH(scan->maxyres);
  476.     } else if (metric == SC_CENTIM) {
  477.     r.maxx = RESINCHTOCM(scan->maxxres);
  478.     r.maxy = RESINCHTOCM(scan->maxyres);
  479.     } else {
  480.     res->errno = SCEBADMETRIC;
  481.     return;
  482.     }
  483.  
  484.     /*
  485.      * Always set the min res to 1; We'll zoom down from some hardware
  486.      * supported resolution to this.
  487.      */
  488.     r.minx = 1;
  489.     r.miny = 1;
  490.     res->data = &r;
  491.     res->len = sizeof(r);
  492. }
  493.  
  494. /*
  495.  *  static void
  496.  *  NumRes(int cmd, SCARG *arg, SCRES *res)
  497.  *
  498.  *  Description:
  499.  *      Inform the scanner application of the number of hardware
  500.  *      resolutions supported.  If this number is 0, we're telling the
  501.  *      application that ALL resolutions are supported in hardware, or
  502.  *      that the scanner itself is capable of doing very high quality
  503.  *      rescaling of the image data as it scans it.
  504.  *
  505.  *  Parameters:
  506.  *      cmd  SCN_NRES
  507.  *      arg  void
  508.  *      res  pointer to int
  509.  */
  510.  
  511. /*ARGSUSED*/
  512. static void
  513. NumRes(int cmd, SCARG *arg, SCRES *res)
  514. {
  515.     res->data = &scan->nres;
  516.     res->len = sizeof(scan->nres);
  517. }
  518.  
  519. /*
  520.  *  static void
  521.  *  HardwareRes(int cmd, SCARG *arg, SCRES *res)
  522.  *
  523.  *  Description:
  524.  *      Inform scanner application of the scanning resolutions
  525.  *      supported by the scanner itself, without having us do
  526.  *      decimation or replication.  Convert metrics as appropriate.
  527.  *
  528.  *  Parameters:
  529.  *      cmd  SCN_RES
  530.  *      arg  metric
  531.  *      res  array of float
  532.  */
  533.  
  534. /*ARGSUSED*/
  535. static void
  536. HardwareRes(int cmd, SCARG *arg, SCRES *res)
  537. {
  538.     static float *r;
  539.     int metric = *(int *)arg->data;
  540.     int i;
  541.  
  542.     if (scan->nres) {
  543.     if (r) {
  544.         free(r);
  545.         r = 0;
  546.     }
  547.     r = calloc(scan->nres * 2, sizeof(*r));
  548.  
  549.     for (i = 0; i < scan->nres; i++) {
  550.         if (metric == scan->metric) {
  551.         r[i] = scan->xres[i];
  552.         r[i + scan->nres] = scan->yres[i];
  553.         } else if (metric == SC_INCHES) {
  554.         r[i] = RESCMTOINCH(scan->xres[i]);
  555.         r[i + scan->nres] = RESCMTOINCH(scan->yres[i]);
  556.         } else if (metric == SC_CENTIM) {
  557.         r[i] = RESINCHTOCM(scan->xres[i]);
  558.         r[i + scan->nres] = RESINCHTOCM(scan->yres[i]);
  559.         } else {
  560.         res->errno = SCEBADMETRIC;
  561.         return;
  562.         }
  563.     }
  564.  
  565.     }
  566.  
  567.     res->len = sizeof(*r) * scan->nres * 2;
  568.     res->data = r;
  569. }
  570.  
  571. /*
  572.  *  static void
  573.  *  NumTypes(int cmd, SCARG *arg, SCRES *res)
  574.  *
  575.  *  Description:
  576.  *      Return to scanner application the number of types supported by
  577.  *      this driver/scanner.
  578.  *
  579.  *  Parameters:
  580.  *      cmd  SCN_NTYPES
  581.  *      arg  void
  582.  *      res  int
  583.  */
  584.  
  585. /*ARGSUSED*/
  586. static void
  587. NumTypes(int cmd, SCARG *arg, SCRES *res)
  588. {
  589.     res->data = &scan->ntypes;
  590.     res->len = sizeof(scan->ntypes);
  591.     return;
  592. }
  593.  
  594. /*
  595.  *  static void
  596.  *  Types(int cmd, SCARG *arg, SCRES *res)
  597.  *
  598.  *  Description:
  599.  *      Tell scanner application what types we support
  600.  *
  601.  *  Parameters:
  602.  *      cmd  SCN_TYPES
  603.  *      arg  void
  604.  *      res  array of SCDATATYPE
  605.  */
  606.  
  607. /*ARGSUSED*/
  608. static void
  609. Types(int cmd, SCARG *arg, SCRES *res)
  610. {
  611.     res->data = scan->types;
  612.     res->len = scan->ntypes * sizeof(SCDATATYPE);
  613. }
  614.  
  615. /*
  616.  *  static void
  617.  *  AbortScan(int cmd, SCARG *arg, SCRES *res)
  618.  *
  619.  *  Description:
  620.  *      Stop scanning.  Induce the child processes to exit.  Arrange
  621.  *      to have ScanCleanup called.
  622.  *
  623.  *  Parameters:
  624.  *      cmd  SCN_ABORT
  625.  *      arg  void
  626.  *      res  void
  627.  */
  628.  
  629. /*ARGSUSED*/
  630. static void
  631. AbortScan(int cmd, SCARG *arg, SCRES *res)
  632. {
  633.     sigset_t mask, omask;
  634.     int stat;
  635.  
  636.     if (status.state != SC_SCANNING) {
  637.     res->errno = SCENOTSCANNING;
  638.     return;
  639.     }
  640.     
  641.     /*
  642.      * Block SIGCHLD while we're waiting for the children to exit, so
  643.      * that SIGCHLD handler won't wait instead.
  644.      */
  645.     (void)sigemptyset(&mask);
  646.     (void)sigaddset(&mask, SIGCHLD);
  647.     (void)sigprocmask(SIG_BLOCK, &mask, &omask);
  648.  
  649.     EndScanning();
  650.  
  651.     (void)waitpid(scanpid, &stat, 0);
  652.     (void)waitpid(imgpid, &stat, 0);
  653.     kids = 0;
  654.  
  655.     /*
  656.      * Restore the old signal mask.
  657.      */
  658.     (void)sigprocmask(SIG_SETMASK, &omask, NULL);
  659.     /*
  660.      * Call SCDriverSyncFunction instead of ScanCleanup so that the
  661.      * app doesn't have to wait for us to do this unless it tries to
  662.      * issue another command
  663.      */
  664.     SCDriverSyncFunction((void (*)(void *))ScanCleanup, NULL);
  665. }
  666.  
  667. /*
  668.  * Forward declaration for StartScan
  669.  */
  670. static void
  671. DoImgProc(SCANPARAMS *params);
  672.  
  673. /*
  674.  *  static void
  675.  *  StartScan(int cmd, SCARG *arg, SCRES *res)
  676.  *
  677.  *  Description:
  678.  *      Start scanning.  Allocate scan buffers and queues, sproc the
  679.  *      children to do the actual work.
  680.  *
  681.  *  Parameters:
  682.  *      cmd  SCN_SCAN
  683.  *      arg  void
  684.  *      res  void
  685.  */
  686.  
  687. static void
  688. StartScan(int cmd, SCARG *arg, SCRES *res)
  689. {
  690.     long scanlinebytes, qlines;
  691.     char *buf;
  692.     sigset_t mask, omask;
  693.  
  694.     if (status.state != SC_READY) {
  695.     res->errno = status.state == SC_SCANNING ? SCEBUSY : drverr;
  696.     if (res->errno == SCEDRVMSG) {
  697.         res->errMsg = drvmsg;
  698.     }
  699.     return;
  700.     }
  701.  
  702.     /*
  703.      * Allocate memory for the scan queue
  704.      *
  705.      * Make sure that each line is aligned on a 4 byte boundary.  This
  706.      * is (so far) the lowest common denominator for allowing this
  707.      * code to work for all encountered scanners; scsi scanners
  708.      * require that buffers passed to the read routine are aligned on
  709.      * 4 byte boundaries, and the screen driver needs this as well for
  710.      * calling readdisplay().
  711.      */
  712.     scanlinebytes = (sparams.xbytes + 3) & ~0x3;
  713.  
  714.     if (!sparams.readlines) {
  715.     sparams.readlines =
  716.         MIN(sparams.maxmem / (scanlinebytes * 2), sparams.ylines);
  717.     
  718.     if (!sparams.readlines) {
  719.         sparams.readlines = 1;
  720.     }
  721.     }
  722.  
  723.     /*
  724.      * If readlines > ylines, no buffers will be put on the freeq in
  725.      * the code below, which will prevent any scanning at all from
  726.      * occurring.
  727.      */
  728.     if (sparams.readlines > sparams.ylines) {
  729.     sparams.readlines = sparams.ylines;
  730.     }
  731.  
  732.     qlines = MIN(sparams.readlines * 2, sparams.ylines);
  733.     
  734.     bytesPinned = qlines * scanlinebytes;
  735.     scanbuf = malloc(bytesPinned);
  736.  
  737.     if (mpin(scanbuf, bytesPinned) < 0) {
  738. #ifdef DEBUG
  739.     perror("mpin");
  740. #endif
  741.     bytesPinned = 0;
  742.     }
  743.  
  744.     sparams.scanq = SCCreateQueue(qlines);
  745.     sparams.sfreeq = SCCreateQueue(qlines / sparams.readlines);
  746.  
  747.     /*
  748.      * Chop the chunk we malloc'd into scanlinebyte-sized chunk *
  749.      * readlines sized chunks.
  750.      */
  751.     buf = scanbuf;
  752.     for (buf = scanbuf;
  753.      qlines >= sparams.readlines;
  754.      qlines -= sparams.readlines) {
  755.     SCEnqueue(sparams.sfreeq, buf);
  756.     buf += scanlinebytes * sparams.readlines;
  757.     }
  758.  
  759.     /*
  760.      * Set things up for DoImgProc
  761.      *
  762.      * SetupScan is supposed to set sparams.convert if any conversion
  763.      * is necessary to achieve one of the four basic types.  The
  764.      * standard conversion routines all do zooming if necessary, so we
  765.      * don't have to worry about choosing an appropriate zoom function
  766.      * if we're already converting.
  767.      */
  768.     convert = sparams.convert;
  769.     if (xpixels != sparams.xpixels) {
  770.     if (!convert) {
  771.         switch (sparams.type.type) {
  772.         case SC_RGB:
  773.         convert = sparams.type.packing == SC_PACKPIX ?
  774.             SCZoomRow24 : SCZoomRow8;
  775.         break;
  776.         case SC_GREY:
  777.         convert = SCZoomRow8;
  778.         break;
  779.         case SC_MONO:
  780.         convert = SCZoomRow1;
  781.         break;
  782.         default:
  783.         drverr = SCEBADTYPE;
  784.         exit(1);
  785.         }
  786.     }
  787.     zmap = SCCreateZoomMap(sparams.xpixels, xpixels);
  788.     }
  789.  
  790.     if (convert) {
  791.     ibuf = malloc(xbytes);
  792.     }
  793.     
  794.     /*
  795.      * Set status BEFORE creating children.  If we call
  796.      * SCDriverPutStatus when we have children, deadlock can occur.
  797.      */
  798.     status.state = SC_SCANNING;
  799.     status.curline = 0;
  800.     SCDriverPutStatus(&status, NULL);
  801.  
  802.     /*
  803.      * We must block SIGCHLD while we're creating child processes,
  804.      * because our SIGCHLD signal handler uses the variable kids to
  805.      * determine whether or not scanning is going on. It's possible
  806.      * that one of our sproc children could exit before we got to the
  807.      * code that incremented kids, so that the count seen by the
  808.      * signal handler would be wrong.
  809.      * 
  810.      * Yes, this actually happened.
  811.      */
  812.     (void)sigemptyset(&mask);
  813.     (void)sigaddset(&mask, SIGCHLD);
  814.     (void)sigprocmask(SIG_BLOCK, &mask, &omask);
  815.  
  816.     /*
  817.      * Set up child processes to do the scanning and zooming.  DoScan
  818.      * gets buffers from sfreeq, scans data into them, and then puts
  819.      * them on scanq.  DoImgProc takes buffers from scanq, zooms and
  820.      * converts them, and dispatches them with SCDriverPutRow.
  821.      */
  822.     imgpid = sproc((void (*)(void *))DoImgProc, PR_SADDR,
  823.            (unsigned)&sparams);
  824.     if (imgpid < 0) {
  825.     (void)sigprocmask(SIG_SETMASK, &omask, 0);
  826.     AbortScan(cmd, arg, res);
  827.     res->errno = errno;
  828.     return;
  829.     }
  830.     kids++;
  831.  
  832.     scanpid = sproc((void (*)(void *))DoScan, PR_SADDR,
  833.             (unsigned)&sparams);
  834.  
  835.     if (scanpid < 0) {
  836.     (void)sigprocmask(SIG_SETMASK, &omask, 0);
  837.     AbortScan(cmd, arg, res);
  838.     res->errno = errno;
  839.     return;
  840.     }
  841.     kids++;
  842.     (void)sigprocmask(SIG_SETMASK, &omask, 0);
  843. }
  844.  
  845. /*
  846.  *  static void
  847.  *  Setup(int cmd, SCARG *arg, SCRES *res)
  848.  *
  849.  *  Description:
  850.  *      Set up the scanner with the scan window and resolution in
  851.  *      anticipation of a pending scan.  Perform all necessary metric
  852.  *      conversions.
  853.  *
  854.  *  Parameters:
  855.  *      cmd  SCN_SETUP
  856.  *      arg  SCSETUP
  857.  *      res  void
  858.  */
  859.  
  860. /*ARGSUSED*/
  861. static void
  862. Setup(int cmd, SCARG *arg, SCRES *res)
  863. {
  864.     SCSETUP *s;
  865.     float xres, yres;
  866.     int i;
  867.  
  868.     s = arg->data;
  869.  
  870.     bzero(&sparams, sizeof sparams);
  871.     sparams.s = scan;
  872.     sparams.preview = s->preview;
  873.     if (scan->metric != s->rmetric) {
  874.     if (scan->metric == SC_INCHES) {
  875.         xres = RESCMTOINCH(s->xres);
  876.         yres = RESCMTOINCH(s->yres);
  877.     } else {
  878.         xres = RESINCHTOCM(s->xres);
  879.         yres = RESINCHTOCM(s->yres);
  880.     }
  881.     } else {
  882.     xres = s->xres;
  883.     yres = s->yres;
  884.     }
  885.  
  886.     /*
  887.      * Set the sparams.xres and sparams.yres to the next highest
  888.      * resolution that the scanner actually supports, or the highest
  889.      * resolution if the scanner doesn't support resolutions higher
  890.      * than those requested.
  891.      */
  892.     if (scan->nres && !scan->canZoom) {
  893.     for (i = 0; i < scan->nres; i++) {
  894.         if (scan->xres[i] >= xres || i == scan->nres - 1) {
  895.         sparams.xres = scan->xres[i];
  896.         break;
  897.         }
  898.     }
  899.  
  900.     for (i = 0; i < scan->nres; i++) {
  901.         if (scan->yres[i] >= yres || i == scan->nres -1) {
  902.         sparams.yres = scan->yres[i];
  903.         break;
  904.         }
  905.     }
  906.     } else {
  907.     sparams.xres = ilimit(xres, scan->minxres, scan->maxxres);
  908.     sparams.yres = ilimit(yres, scan->minyres, scan->maxyres);
  909.     }
  910.  
  911.     switch (s->wmetric) {
  912.     case SC_PIXELS:
  913.     sparams.x = s->x / xres;
  914.     sparams.y = s->y / yres;
  915.     sparams.width = s->width / xres;
  916.     sparams.height = s->height / yres;
  917.     xpixels = s->width;
  918.     ysize = s->height;
  919.     break;
  920.     case SC_INCHES:
  921.     if (scan->metric == SC_INCHES) {
  922.         sparams.x = s->x;
  923.         sparams.y = s->y;
  924.         sparams.width = s->width;
  925.         sparams.height = s->height;
  926.     } else {
  927.         sparams.x = INCHTOCM(s->x);
  928.         sparams.y = INCHTOCM(s->y);
  929.         sparams.width = INCHTOCM(s->width);
  930.         sparams.height = INCHTOCM(s->height);
  931.     }
  932.     xpixels = s->width * (s->rmetric == SC_INCHES ? s->xres :
  933.                   RESCMTOINCH(s->xres));
  934.     ysize = s->height * (s->rmetric == SC_INCHES ? yres :
  935.                  RESCMTOINCH(s->yres));
  936.     break;
  937.     case SC_CENTIM:
  938.     if (scan->metric == SC_CENTIM) {
  939.         sparams.x = s->x;
  940.         sparams.y = s->y;
  941.         sparams.width = s->width;
  942.         sparams.height = s->height;
  943.     } else {
  944.         sparams.x = CMTOINCH(s->x);
  945.         sparams.y = CMTOINCH(s->y);
  946.         sparams.width = CMTOINCH(s->width);
  947.         sparams.height = CMTOINCH(s->height);
  948.     }
  949.     xpixels = s->width * (s->rmetric == SC_CENTIM ? s->xres :
  950.                   RESINCHTOCM(s->xres));
  951.     ysize = s->height * (s->rmetric == SC_CENTIM ? yres :
  952.                  RESINCHTOCM(s->yres));
  953.     break;
  954.     default:
  955.     res->errno = SCENOTSUPPORTED;
  956.     return;
  957.     }
  958.  
  959.     sparams.type = s->type;
  960.  
  961.     sparams.maxmem = maxmem;
  962.     if (SetupScan(&sparams) < 0) {
  963.     res->errno = drverr;
  964.     if (drverr == SCEDRVMSG) {
  965.         res->errMsg = drvmsg;
  966.     }
  967.     return;
  968.     }
  969.  
  970.     /*
  971.      * Don't do any zooming if we don't have to.  Our calculations
  972.      * above may have yielded slightly different results than similar
  973.      * calculations in the scanner, and unless we were asked to return
  974.      * a specific number of pixels, we figure that image quality is
  975.      * the over-riding concern.
  976.      */
  977.     if (xres == sparams.xres && yres == sparams.yres && s->wmetric !=
  978.     SC_PIXELS) {
  979.     xpixels = sparams.xpixels;
  980.     ysize = sparams.ylines;
  981.     }
  982.  
  983.     /*
  984.      * Return an error if this scan would be a no-op.
  985.      */
  986.     if (sparams.xpixels == 0 || sparams.ylines == 0 || xpixels == 0 ||
  987.     ysize == 0) {
  988.     res->errno = SCEAREATOOSMALL;
  989.     return;
  990.     }
  991.  
  992.     if (ISRGBPIX8(&s->type)) {
  993.     xbytes = xpixels * 3;
  994.     } else if (ISRGBPIX16(&s->type)) {
  995.     xbytes = xpixels * 6;
  996.     } else if (ISRGBPIX1(&s->type)) {
  997.     xbytes = (xpixels + 1) / 2;
  998.     } else if (ISRGBPLANE8(&s->type) || ISGREY8(&s->type)) {
  999.     xbytes = xpixels;
  1000.     } else if (ISRGBPLANE16(&s->type) || ISGREY16(&s->type)) {
  1001.     xbytes = xpixels * 2;
  1002.     } else if (ISMONO1(&s->type)) {
  1003.     xbytes = (xpixels + 7) / 8;
  1004.     } else {
  1005.     res->errno = SCEBADTYPE;
  1006.     return;
  1007.     }
  1008. }
  1009.  
  1010. /*
  1011.  *  static void
  1012.  *  GetSize(int cmd, SCARG *arg, SCRES *res)
  1013.  *
  1014.  *  Description:
  1015.  *      Return to the scanning application the size of the current
  1016.  *      scan, that set by the last call to Setup.
  1017.  *
  1018.  *  Parameters:
  1019.  *      cmd  SCN_GETSIZE
  1020.  *      arg  void
  1021.  *      res  SCSIZE
  1022.  */
  1023.  
  1024. /*ARGSUSED*/
  1025. static void
  1026. GetSize(int cmd, SCARG *arg, SCRES *res)
  1027. {
  1028.     static SCSIZE sz;
  1029.  
  1030.     sz.xbytes = xbytes;
  1031.     sz.xpixels = xpixels;
  1032.     sz.ysize = ysize;
  1033.     res->data = &sz;
  1034.     res->len = sizeof sz;
  1035. }
  1036.  
  1037. /*
  1038.  *  static void
  1039.  *  PageSize(int cmd, SCARG *arg, SCRES *res)
  1040.  *
  1041.  *  Description:
  1042.  *      Inform the scanning application of the (x,y,width,height)
  1043.  *      coordinates of the supported scanning area, converting between
  1044.  *      metrics as necessary
  1045.  *
  1046.  *  Parameters:
  1047.  *      cmd  SCN_PAGESIZE
  1048.  *      arg  metric
  1049.  *      res  SCWINDOW
  1050.  */
  1051.  
  1052. /*ARGSUSED*/
  1053. static void
  1054. PageSize(int cmd, SCARG *arg, SCRES *res)
  1055. {
  1056.     static SCWINDOW w = { 0, 0, 0, 0};
  1057.     int metric = *(int *)arg->data;
  1058.  
  1059.     if (metric != SC_INCHES && metric != SC_CENTIM) {
  1060.     res->errno = SCEBADMETRIC;
  1061.     return;
  1062.     }
  1063.  
  1064.     if (metric == scan->metric) {
  1065.     w.x = scan->pagex;
  1066.     w.y = scan->pagey;
  1067.     w.width = scan->pagewidth;
  1068.     w.height = scan->pageheight;
  1069.     } else {
  1070.     if (metric == SC_INCHES) {
  1071.         w.x = CMTOINCH(scan->pagex);
  1072.         w.y = CMTOINCH(scan->pagey);
  1073.         w.width = CMTOINCH(scan->pagewidth);
  1074.         w.height = CMTOINCH(scan->pageheight);
  1075.     } else {
  1076.         w.x = INCHTOCM(scan->pagex);
  1077.         w.y = INCHTOCM(scan->pagey);
  1078.         w.width = INCHTOCM(scan->pagewidth);
  1079.         w.height = INCHTOCM(scan->pageheight);
  1080.     }
  1081.     }
  1082.  
  1083.     res->data = &w;
  1084.     res->len = sizeof w;
  1085. }
  1086.  
  1087. /*
  1088.  *  void
  1089.  *  FeederGetFlags(int cmd, SCARG *arg, SCRES *res)
  1090.  *
  1091.  *  Description:
  1092.  *      Get this scanner's feeder flags for the scanner application
  1093.  *
  1094.  *  Parameters:
  1095.  *      cmd  SCN_FEEDERGETFLAGS
  1096.  *      arg  none
  1097.  *      res  SCFEEDERFLAGS *
  1098.  */
  1099.  
  1100. /*ARGSUSED*/
  1101. void
  1102. FeederGetFlags(int cmd, SCARG *arg, SCRES *res)
  1103. {
  1104.     res->data = &scan->feederFlags;
  1105.     res->len = sizeof scan->feederFlags;
  1106. }
  1107.  
  1108. /*
  1109.  *  void
  1110.  *  FeederSetFlags(int cmd, SCARG *arg, SCRES *res)
  1111.  *
  1112.  *  Description:
  1113.  *      Set the feeder flags passed by the scanner application.  This
  1114.  *      is used to select automatic or programmatic feeding for
  1115.  *      scanners that support both.
  1116.  *
  1117.  *  Parameters:
  1118.  *      cmd  SCN_FEEDERSETFLAGS
  1119.  *      arg  SCFEEDERFLAGS *
  1120.  *      res  none
  1121.  */
  1122.  
  1123. /*ARGSUSED*/
  1124. void
  1125. FeederSetFlags(int cmd, SCARG *arg, SCRES *res)
  1126. {
  1127.     if (!(scan->feederFlags & SC_HASFEEDER)) {
  1128.     res->errno = SCENOFEEDER;
  1129.     return;
  1130.     }
  1131.     if (SetFeederFlags(scan, *(SCFEEDERFLAGS *)arg->data) < 0) {
  1132.     res->errno = drverr;
  1133.     if (drverr == SCEDRVMSG) {
  1134.         res->errMsg = drvmsg;
  1135.     }
  1136.     }
  1137. }
  1138.  
  1139. /*
  1140.  *  void
  1141.  *  FeederAdvance(int cmd, SCARG *arg, SCRES *res)
  1142.  *
  1143.  *  Description:
  1144.  *      Advance the feeder to the next document
  1145.  *
  1146.  *  Parameters:
  1147.  *      cmd  SCN_FEEDERADVANCE
  1148.  *      arg  none
  1149.  *      res  none
  1150.  */
  1151.  
  1152. /*ARGSUSED*/
  1153. void
  1154. FeederAdvance(int cmd, SCARG *arg, SCRES *res)
  1155. {
  1156.     if (!(scan->feederFlags & SC_HASFEEDER)) {
  1157.     res->errno = SCENOFEEDER;
  1158.     return;
  1159.     }
  1160.     if (AdvanceFeeder(scan) < 0) {
  1161.     res->errno = drverr;
  1162.     if (drverr == SCEDRVMSG) {
  1163.         res->errMsg = drvmsg;
  1164.     }
  1165.     }
  1166. }
  1167.  
  1168. /*
  1169.  *  void
  1170.  *  IsFeederReady(int cmd, SCARG *arg, SCRES *res)
  1171.  *
  1172.  *  Description:
  1173.  *      Find out whether the document feeder is ready to be advanced;
  1174.  *      that is, whether a call to FeederAdvance would be successful.
  1175.  *
  1176.  *  Parameters:
  1177.  *      cmd  SCN_FEEDERREADY
  1178.  *      arg  none
  1179.  *      res  none
  1180.  */
  1181.  
  1182. /*ARGSUSED*/
  1183. void
  1184. IsFeederReady(int cmd, SCARG *arg, SCRES *res)
  1185. {
  1186.     if (!(scan->feederFlags & SC_HASFEEDER)) {
  1187.     res->errno = SCENOFEEDER;
  1188.     return;
  1189.     }
  1190.     if (FeederReady(scan) < 0) {
  1191.     res->errno = drverr;
  1192.     if (drverr == SCEDRVMSG) {
  1193.         res->errMsg = drvmsg;
  1194.     }
  1195.     }
  1196. }
  1197.  
  1198. /*
  1199.  *  void
  1200.  *  GetVersion(int cmd, SCARG *arg, SCRES *res)
  1201.  *
  1202.  *  Description:
  1203.  *      Tell the scanner app what version of libscan we linked with.
  1204.  *
  1205.  *  Parameters:
  1206.  *      cmd  SCN_GETVERSION
  1207.  *      arg  none
  1208.  *      res  float *
  1209.  */
  1210.  
  1211. /*ARGSUSED*/
  1212. void
  1213. GetVersion(int cmd, SCARG *arg, SCRES *res)
  1214. {
  1215.     static float version = SC_VERSION;
  1216.  
  1217.     res->data = &version;
  1218.     res->len = sizeof version;
  1219. }
  1220.  
  1221. /*
  1222.  *  void
  1223.  *  GetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
  1224.  *
  1225.  *  Description:
  1226.  *      Get options to save from the scanner.  This function is used
  1227.  *      for two different IPCs: SCN_GETSAVEOPTLEN and SCN_GETSAVEOPT.
  1228.  *      We store the return value for SCN_GETSAVEOPT when
  1229.  *      SCN_GETSAVEOPTLEN is called, so in case anything happens that
  1230.  *      changes the size of the saveable options between these two
  1231.  *      IPC's we don't end up writing inconsistent data back to the
  1232.  *      app (although it will be stale).
  1233.  *
  1234.  *      In practice these two commands should come one after the
  1235.  *      other, so there shouldn't be any problem with storing the data
  1236.  *      for a very short period of time.
  1237.  *
  1238.  *      This also makes the interface with the scan.c portion of this
  1239.  *      command very simple, which is very desirable.
  1240.  *
  1241.  *  Parameters:
  1242.  *      cmd  SCN_GETSAVEOPTLEN or SCN_GETSAVEOPT
  1243.  *      arg  NULL
  1244.  *      res  int *             or void *
  1245.  */
  1246.  
  1247. void
  1248. GetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
  1249. {
  1250.     static int nBytes;
  1251.     static void *optBuf = NULL, *options;
  1252.  
  1253.     if (cmd == SCN_GETSAVEOPTLEN) {
  1254.     options = GetSaveOptions(scan, &nBytes);
  1255.     
  1256.     if (!options) {
  1257.         res->errno = drverr;
  1258.         if (drverr == SCEDRVMSG) {
  1259.         res->errMsg = drvmsg;
  1260.         }
  1261.         return;
  1262.     }
  1263.     
  1264.     if (optBuf) {
  1265.         free(optBuf);
  1266.     }
  1267.     optBuf = malloc(nBytes);
  1268.     bcopy(options, optBuf, nBytes);
  1269.     res->data = &nBytes;
  1270.     res->len = sizeof nBytes;
  1271.     } else {
  1272.     /*
  1273.      * This is actually a protocol error; the client libscan
  1274.      * should have called SCN_GETSAVEOPTLEN before calling
  1275.      * SCN_GETSAVEOPT
  1276.      */
  1277.     if (!options) {
  1278.         res->errno = SCENOSAVEOPT;
  1279.         return;
  1280.     }
  1281.     res->data = optBuf;
  1282.     res->len = nBytes;
  1283.     }
  1284. }
  1285.  
  1286. /*
  1287.  *  void
  1288.  *  SetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
  1289.  *
  1290.  *  Description:
  1291.  *      Set options based on saved values from the scanner application
  1292.  *
  1293.  *  Parameters:
  1294.  *      cmd  SCN_SETSAVEOPT
  1295.  *      arg  void *
  1296.  *      res  NULL
  1297.  */
  1298.  
  1299. void
  1300. SetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
  1301. {
  1302.     if (SetSaveOptions(scan, arg->data, arg->len) == -1) {
  1303.     res->errno = drverr;
  1304.     if (drverr == SCEDRVMSG) {
  1305.         res->errMsg = drvmsg;
  1306.     }
  1307.     }
  1308. }
  1309.  
  1310. /*
  1311.  * Table of functions to be passed to the scan library.  Order is of
  1312.  * the utmost importance here; the position of each function in this
  1313.  * table corresponds to the SCN_ #define in <scanipc.h> of the
  1314.  * command that it implements.
  1315.  */
  1316. SCANFUNC scanTable[] = {
  1317.     InitOK,
  1318.     Die,
  1319.     ResMinMax,
  1320.     NumRes,
  1321.     HardwareRes,
  1322.     NumTypes,
  1323.     Types,
  1324.     PageSize,
  1325.     Setup,
  1326.     StartScan,
  1327.     AbortScan,
  1328.     GetSize,
  1329.     FeederGetFlags,
  1330.     FeederSetFlags,
  1331.     FeederAdvance,
  1332.     IsFeederReady,
  1333.     GetVersion,
  1334.     /*
  1335.      * GetSaveOptionsCB is here twice because it's the callback for
  1336.      * two separate commands.
  1337.      */
  1338.     GetSaveOptionsCB,
  1339.     GetSaveOptionsCB,
  1340.     SetSaveOptionsCB
  1341. };
  1342.  
  1343. /*
  1344.  *  static void
  1345.  *  usage(char *pn)
  1346.  *
  1347.  *  Description:
  1348.  *      Print a usage line for a scanner driver
  1349.  *
  1350.  *  Parameters:
  1351.  *      pn  program name to print usage for
  1352.  */
  1353.  
  1354. static void
  1355. usage(char *pn)
  1356. {
  1357.     (void)fprintf(stderr, "usage: %s -query\n", pn);
  1358.     (void)fprintf(stderr, "       %s -version\n", pn);
  1359.     (void)fprintf(stderr, "       %s -install <device>\n", pn);
  1360.     (void)fprintf(stderr, "       %s -delete <device>\n", pn);
  1361.     (void)fprintf(stderr, "       %s <device> <arena>\n", pn);
  1362. }
  1363.  
  1364. /*
  1365.  *  int
  1366.  *  main(int argc, char *argv[])
  1367.  *
  1368.  *  Description:
  1369.  *      main routine for the scanner driver.  Should be called
  1370.  *      with argv[1] == name of the device which corresponds to the
  1371.  *      scanner to open and argv[2] == name of the us arena file used
  1372.  *      for communication with the scanning application.
  1373.  *
  1374.  *  Parameters:
  1375.  *      argc - number of command line arguments
  1376.  *      argv - command line arguments
  1377.  *
  1378.  *  Returns:
  1379.  *      exit status
  1380.  */
  1381.  
  1382. int
  1383. main(int argc, char *argv[])
  1384. {
  1385.     struct sigaction act;
  1386.  
  1387.     drverr = 0;
  1388.  
  1389.     (void)setlocale(LC_ALL, "");
  1390.  
  1391.     if (argc < 2) {
  1392.     usage(argv[0]);
  1393.     return 1;
  1394.     }
  1395.  
  1396.     if (strcmp(argv[1], "-query") == 0) {
  1397.     PrintID(stdout);
  1398.     FindScanners(stdout);
  1399.     exit(0);
  1400.     } else if (strcmp(argv[1], "-version") == 0) {
  1401.     /*
  1402.      * Don't change this.  The install tool may use this
  1403.      * information in future versions of Impressario if the
  1404.      * communications protocol between clients and applications
  1405.      * change.
  1406.      */
  1407.     (void)printf("%g\n", SC_VERSION);
  1408.     exit(0);
  1409.     } else if (strcmp(argv[1], "-install") == 0) {
  1410.     if (argc != 3) {
  1411.         usage(argv[0]);
  1412.         return 1;
  1413.     }
  1414.     return InstallScanner(argv[2]) == 0 ? 0 : 1;
  1415.     } else if (strcmp(argv[1], "-delete") == 0) {
  1416.     if (argc != 3) {
  1417.         usage(argv[0]);
  1418.         return 1;
  1419.     }
  1420.     return DeleteScanner(argv[2]) == 0 ? 0 : 1;
  1421.     }
  1422.  
  1423.     if (argc != 3) {
  1424.     usage(argv[0]);
  1425.     return 1;
  1426.     }
  1427.  
  1428.     if (SCDriverInit(argv[2]) < 0) {
  1429.     drverr = errno;
  1430.     } else {
  1431.     scan = OpenScanner(argv[1]);
  1432.  
  1433.     SetMaxMem();
  1434.     
  1435.     act.sa_handler = childdeath;
  1436.     sigemptyset(&act.sa_mask);
  1437.     sigaddset(&act.sa_mask, SIGCHLD);
  1438.     act.sa_flags = 0;
  1439.     
  1440.     (void)sigaction(SIGCHLD, &act, NULL);
  1441.     
  1442.     status.state = SC_READY;
  1443.     status.curline = 0;
  1444.     status.pass = 0;
  1445.     SCDriverPutStatus(&status, NULL);
  1446.     }
  1447.  
  1448.     /*
  1449.      * Properly handle SIGHUP, which will get sent to us if our
  1450.      * parent, which is the scanning application, exits.
  1451.      */
  1452.     act.sa_handler = hangup;
  1453.     sigemptyset(&act.sa_mask);
  1454.     act.sa_flags = 0;
  1455.     (void)sigaction(SIGHUP, &act, NULL);
  1456.     (void)sigaction(SIGQUIT, &act, NULL);
  1457.  
  1458.     /*
  1459.      * Call setsid, so we don't get signals propogated to us if our
  1460.      * parent dies.  When that happens, we will get a SIGHUP, and
  1461.      * we'll clean up from there.
  1462.      */
  1463.     setsid();
  1464.  
  1465.     SCDriverSetCallbacks(scanTable, sizeof(scanTable)/sizeof(*scanTable),
  1466.              scan ? scan->options : NULL, scan ?
  1467.              scan->noptions : 0);
  1468.     SCDriverMainLoop();
  1469.     return 0;
  1470. }
  1471.  
  1472. /*
  1473.  *  static void
  1474.  *  DoImgProc(SCANPARAMS *params)
  1475.  *
  1476.  *  Description:
  1477.  *      Take the scanned data off of params->scanq, scale and convert
  1478.  *      it to specification, and call SCDriverPutRow() with the
  1479.  *      result.
  1480.  *
  1481.  *      This is meant to be passed to sproc so that it runs as a child
  1482.  *      thread of the main driver thread.  It exits when it's done,
  1483.  *      with a status of 0 if everything was OK and a status of 1 if
  1484.  *      something went wrong.  When exiting with a status of 1, we set
  1485.  *      drverr to indicate what the problem was.
  1486.  *
  1487.  *      WARNING:
  1488.  *      Calls to SCEnqueue, SCDequeue, and SCDriverPutRow might not
  1489.  *      return; ie these functions might call exit.  If you modify
  1490.  *      this code, be careful that you don't strand any resources due
  1491.  *      to one of these three functions exiting.
  1492.  *
  1493.  *  Parameters:
  1494.  *      params  parameters describing the scan
  1495.  */
  1496.  
  1497. static void
  1498. DoImgProc(SCANPARAMS *params)
  1499. {
  1500.     int imgy, scany, cury;
  1501.     float fy;
  1502.     char *buf;
  1503.     char *tofree = 0;
  1504.     int npasses;
  1505.     int scanChunk;
  1506.  
  1507.     SCDriverInitChild();
  1508.     (void)prctl(PR_TERMCHILD);
  1509.  
  1510.     /*
  1511.      * scanChunk is the number of bytes in a chunk that sits on the
  1512.      * scanq.  The scanning thread will typically scan many lines at a
  1513.      * time for efficiency, and break up the buffer into line
  1514.      * components for us to digest.  It's then our job to put the
  1515.      * lines back together for the scanning thread.
  1516.      */
  1517.     scanChunk = ((params->xbytes + 3) & ~3) * params->readlines;
  1518.  
  1519.     cury = -1;
  1520.     imgy = 0;
  1521.     npasses = sparams.type.type == SC_RGB && sparams.type.packing ==
  1522.            SC_PACKPLANE ? 3 : 1;
  1523.  
  1524.     while (npasses--) {
  1525.     while (imgy < ysize) {
  1526.         /*
  1527.          * Zoom in the vertical direction; this involves either
  1528.          * repeating rows or skipping rows.  We use GRIDTOFLOAT and
  1529.          * FLOATTOGRID to figure out which row we want this iteration,
  1530.          * and then skip rows until we get the right one.
  1531.          */
  1532.         fy = GRIDTOFLOAT(imgy, ysize);
  1533.         scany = FLOATTOGRID(fy, params->ylines);
  1534.         while(cury < scany) {
  1535.         cury++;
  1536.         if (cury % params->readlines == 0 && tofree) {
  1537.             SCEnqueue(params->sfreeq, tofree);
  1538.             tofree = 0;
  1539.         }
  1540.         buf = SCDequeue(params->scanq);
  1541.         if (((int)buf - (int)scanbuf) % scanChunk == 0) {
  1542.             /*
  1543.              * This should never happen.  As long as tofree
  1544.              * occurs somewhere in each readlines lines, and
  1545.              * the tofree from one group of lines doesn't end
  1546.              * up in another group, we won't get here.
  1547.              */
  1548.             if (tofree) {
  1549.             SCEnqueue(params->sfreeq, tofree);
  1550.             tofree = 0;
  1551.             }
  1552.             tofree = buf;
  1553.         }
  1554.         }
  1555.         
  1556.         if (convert) {
  1557.         convert(buf, params->xpixels, ibuf, xpixels, zmap);
  1558.         }
  1559.         
  1560.         if (SCDriverPutRow(convert ? ibuf : buf, xbytes) < 0) {
  1561.         drverr = errno;
  1562.         exit(1);
  1563.         }
  1564.         
  1565.         imgy = imgy + 1 % ysize;
  1566.         status.curline = imgy;
  1567.         status.pass = imgy / ysize;
  1568.         
  1569.         SCDriverPutStatus(&status, NULL);
  1570.     }
  1571.  
  1572.     /*
  1573.      * Grab any lines at the end that we're skipping.  We need to
  1574.      * do this so that the scanning thread doesn't get wedged
  1575.      * trying to enqueue scanned buffers, and also to clear things
  1576.      * out for multi-pass scanners.
  1577.      */
  1578.     while (cury++ < params->ylines - 1) {
  1579.         if (cury % params->readlines == 0 && tofree) {
  1580.         SCEnqueue(params->sfreeq, tofree);
  1581.         tofree = 0;
  1582.         }
  1583.         buf = SCDequeue(params->scanq);
  1584.         if (((int)buf - (int)scanbuf) % scanChunk == 0) {
  1585.         /*
  1586.          * This should never happen.  As long as tofree
  1587.          * occurs somewhere in each readlines lines, and
  1588.          * the tofree from one group of lines doesn't end
  1589.          * up in another group, we won't get here.
  1590.          */
  1591.         if (tofree) {
  1592.             SCEnqueue(params->sfreeq, tofree);
  1593.             tofree = 0;
  1594.         }
  1595.         tofree = buf;
  1596.         }
  1597.     }
  1598.  
  1599.     cury = -1;
  1600.     imgy = 0;
  1601.     }
  1602.  
  1603.     /*
  1604.      * Not strictly necessary (because ScanCleanup destroys the queue
  1605.      * and frees the memory), but still good form.
  1606.      */
  1607.     if (tofree) {
  1608.     SCEnqueue(params->sfreeq, tofree);
  1609.     tofree = 0;
  1610.     }
  1611.     exit(0);
  1612. }
  1613.  
  1614.